package org.molamil.acto.handlers {

	import org.molamil.acto.core.events.ActoEvent;
	import org.molamil.acto.core.loggers.Logger;
	import org.molamil.acto.core.registry.RegisteredTarget;
	import org.molamil.acto.core.session.ActoSession;
	import org.molamil.acto.params.ParamContext;
	import org.molamil.acto.utils.ObjectUtils;
	import org.molamil.acto.utils.StringUtils;

	import flash.events.Event;
	import flash.events.EventDispatcher;

	/**
	 * @author Jorge Hernandez
	 */
	public class DefaultHandlersController extends EventDispatcher implements HandlersController {


		// CONSTANTS
		
		private static const log:Logger = Logger.getLogger("DefaultHandlersController");
		
		
		// PROPERTIES 
		
		protected var session:ActoSession;
		
		private var _handlers:Array; // of HandlerContext
		
		
		// CONSTRUCTOR
		
		public function DefaultHandlersController(session:ActoSession) {
			this.session = session;
            _handlers = new Array();
		}
		
		
		// PUBLIC METHODS
		
		public function registerHandlerContext(handlerContext:HandlerContext):void {
			if (indexOfHandlerContext(handlerContext) == -1) {
				handlerContext.priority = handlerContext.actionResolver.resolveHandlerPriority(handlerContext);
				_handlers.push(handlerContext);
				session.root.addEventListener(handlerContext.type, centralHandler, false, handlerContext.priority, true);
			} else {
				log.warning("There is already a handler added for \"{id}\" with type \"{type}\"".replace("{id}", handlerContext.context.id).replace("{type}", handlerContext.type));
			}
		}
		
		public function unregisterHandlerContext(handlerContext:HandlerContext):void {
			if (indexOfHandlerContext(handlerContext) != -1) {
				_handlers.splice(_handlers.indexOf(handlerContext), 1);
			} else {
				log.warning("There is no handler added for \"{id}\" with type \"{type}\"".replace("{id}", handlerContext.context.id).replace("{type}", handlerContext.type));
			}
		}
		
		public function unregisterAllHandlerContexts():void {
			_handlers = new Array();
		}

		
		// PROTECTED METHODS
		
		protected function getFilteredParams(handlerContext:HandlerContext, event:Event):Object {
			var o:Object = new Object();
			for each (var paramContext:ParamContext in handlerContext.params) {
				var oName:String = (paramContext.asName != null) ? paramContext.asName : paramContext.name;
				if (paramContext.value === false || (paramContext.value !== null && paramContext.value != "")) {
					o[oName] = paramContext.value;
				} else if (ObjectUtils.containsInstancePropertyOrAccessor(event, paramContext.name)) {
					o[oName] = event[paramContext.name];
				} else {
					log.warning("There is no value to be passed for the event \"{event}\" on the parameter \"{param}\"".replace("{event}", handlerContext.type).replace("{param}", paramContext.name));
				}
			}
			return o;
		}
		
		protected function matchSource(handlerContext:HandlerContext, event:Event):Boolean {
			var b:Boolean = false;
			if (handlerContext.source != null && StringUtils.trim(handlerContext.source) != null) {
				var registeredTarget:RegisteredTarget;
				if (event is ActoEvent) {
					registeredTarget = session.targetRegistry.getRegisteredTarget(ActoEvent(event).targetObject);
				} else {
					registeredTarget = session.targetRegistry.getRegisteredTarget(event.target);
				}
				if (registeredTarget != null && registeredTarget.id == StringUtils.trim(handlerContext.source))
					b = true;
			} else {
				b = true;
			}	
			return b;
		}
		
		protected function indexOfHandlerContext(handlerContext:HandlerContext):int {
			var index:int = -1;
			for (var i:int = 0; i < _handlers.length; i++) {
				var ec:HandlerContext = _handlers[i];
				if (handlerContext.type == ec.type && handlerContext.className == ec.className && handlerContext.action == ec.action && handlerContext.context == ec.context && handlerContext.source == ec.source) {
					index = i;
					break;
				}
			}
			return index;
		}
		
		
		// EVENT HANDLERS
		
		public function centralHandler(event:Event):void {
			var orderedHandlers:Array = new Array();
			for each (var handlerContext:HandlerContext in handlers) {
				if (handlerContext.type == event.type && matchSource(handlerContext, event)) {
					var i:int;
					for (i = 0; i < orderedHandlers.length; i++) {
						if (HandlerContext(orderedHandlers[i]).priority < handlerContext.priority)
							break;
					}
					orderedHandlers.splice(i, 0, handlerContext);
				}
			}
			for each (handlerContext in orderedHandlers)
				handlerContext.actionResolver.resolveHandlerAction(handlerContext, getFilteredParams(handlerContext, event));
		}
		
		
		// GETTER/SETTER
		
		public function get handlers():Array {
			return _handlers;
		}
		
		
	}
	
}
